package main

import (
	"global/libs/ws"
	"global/constants"
	"encoding/json"
	"time"
	"strings"
	"global/core"
	"fmt"
	"github.com/shopspring/decimal"
	"github.com/ngaut/log"
	"global/util"
)

type PairDetail struct {
	Handler  		*Handler
	exit 			chan interface{}

	exchangeName 	string
	from 			string
	to 				string
	depthLength 	int
	klineInterval 	string
	klineLength 	int

	isDepthRunning 	bool
	isKlineRunning 	bool

	summaryTicker 	*time.Ticker
	klineTicker 	*time.Ticker
	depthTicker 	*time.Ticker
}

func (h *PairDetail) ReadWriteMessage(handler *Handler, message ws.RequestMessage) error {
	h.Handler = handler
	err := h.dispatch(message)
	if err != nil {
		return err
	}
	return nil
}

func (h *PairDetail) dispatch(message ws.RequestMessage) error {
	bytesMsg := []byte(message.Message)
	var err error
	switch message.MessageId {
	case constants.RequestPairDetailSummary:
		var req ws.PairDetailSummaryRequest
		if err = json.Unmarshal(bytesMsg, &req); err == nil {
			h.handleSummary(&req)
		}
	case constants.RequestPairDetailKline:
		var req  ws.PairDetailKlineRequest
		if err = json.Unmarshal(bytesMsg, &req); err == nil {
			h.handleKline(&req)
		}
	case constants.RequestPairDetailDepth:
		var req ws.PairDetailDepthRequest
		if err = json.Unmarshal(bytesMsg, &req); err == nil {
			h.handleDepth(&req)
		}
	}
	return err
}

func (h *PairDetail) handleSummary(request *ws.PairDetailSummaryRequest)  {

	if request.From == "" || request.To == "" || request.Exchange == "" {
		h.Handler.response <- ws.ErrorMsg(ws.ErrParameter, constants.RequestPairDetailSummary)
		return
	}

	var runTicker bool
	if h.from == "" || h.to == "" || h.exchangeName == "" {
		runTicker = true
	}

	h.from = strings.ToLower(request.From)
	h.to = strings.ToLower(request.To)
	h.exchangeName = strings.ToLower(request.Exchange)

	h.summary()
	if !runTicker {
		return
	}

	go func() {
		for t := range h.summaryTicker.C {
			_ = t
			h.summary()
		}
	}()
}

func (h *PairDetail) summary() {
	conn := h.Handler.ctx.GetBaseRedis().Get()
	defer conn.Close()

	ticker, err := core.GetPairTickerFromExchange(conn, h.exchangeName, h.from + "_" + h.to)
	if err != nil {
		log.Errorf("Error: %s", err)
		return
	}

	usd, _ := decimal.NewFromString(ticker.PriceUsd)
	cny, _ := decimal.NewFromString(ticker.PriceCny)

	if usd.Equal(decimal.Zero) || cny.Equal(decimal.Zero) { return }

	op, _ := decimal.NewFromString(ticker.Open)
	hp, _ := decimal.NewFromString(ticker.High)
	lp, _ := decimal.NewFromString(ticker.Low)
	cp, _ := decimal.NewFromString(ticker.Close)

	p := &ws.PairDetailSummaryResponse{
		From: 			strings.ToUpper(h.from),
		To: 			strings.ToUpper(h.to),
		Symbol: 		fmt.Sprintf("%s/%s", strings.ToUpper(h.from), strings.ToUpper(h.to)),
		Volume24h: 		ticker.Volume,
		Amount24h: 		ticker.Amount,
		Amount24hUsd: 	ticker.AmountUsd,
		Amount24hCny: 	ticker.AmountCny,
		Open24h: 		ticker.Open,
		Open24hUsd: 	usd.Mul(op).Div(cp).StringFixedBank(4),
		Open24hCny: 	cny.Mul(op).Div(cp).StringFixedBank(4),
		High24h: 		ticker.High,
		High24hUsd: 	usd.Mul(hp).Div(cp).StringFixedBank(4),
		High24hCny: 	cny.Mul(hp).Div(cp).StringFixedBank(4),
		Low24h: 		ticker.Low,
		Low24hUsd: 		usd.Mul(lp).Div(cp).StringFixedBank(4),
		Low24hCny: 		cny.Mul(lp).Div(cp).StringFixedBank(4),
		Close24h: 		ticker.Close,
		Close24hUsd: 	ticker.PriceUsd,
		Close24hCny: 	ticker.PriceCny,
		Percent: 		ticker.Percent,
	}

	h.Handler.response <- ws.WsResponse(p, constants.RequestPairDetailSummary)
}

func (h *PairDetail) handleKline(request *ws.PairDetailKlineRequest) {
	// check params
	if request.Exchange == "" || request.From == "" || request.To == "" {
		h.Handler.response <- ws.ErrorMsg(ws.ErrParameter, constants.RequestPairDetailKline)
		return
	}

	if _, ok := util.IntervalMap[request.Interval]; !ok {
		h.Handler.response <- ws.ErrorMsg(ws.ErrParameter, constants.RequestPairDetailKline)
		return
	}


	h.from = strings.ToLower(request.From)
	h.to = strings.ToLower(request.To)
	h.exchangeName = request.Exchange
	h.klineInterval = request.Interval
	h.klineLength = request.Length
	if request.Length < 200 || request.Length > 1000 {
		h.klineLength = 200
	}

	h.kline()

	if !h.isKlineRunning {
		go func() {
			for t := range h.klineTicker.C {
				_ = t
				h.kline()
			}
		}()
		h.isKlineRunning = true
	}
}

// 获取交易对K线
func (h *PairDetail) kline()  {
	if h.klineInterval == "" || h.klineLength < 200 { return }

	conn := h.Handler.ctx.GetBaseRedis().Get()
	defer conn.Close()

	lists, err := core.GetAnalysisKlinePairLists(conn, h.exchangeName, h.klineInterval, h.from + "_" + h.to, h.klineLength)
	if err != nil {
		log.Errorf("Error: %s", err)
		return
	}

	h.Handler.response<-ws.WsResponse(lists, constants.RequestPairDetailKline)
}

func (h *PairDetail) handleDepth(request *ws.PairDetailDepthRequest) {
	// check slug
	if request.Exchange == "" || request.From == "" || request.To == "" {
		h.Handler.response <- ws.ErrorMsg(ws.ErrParameter, constants.RequestPairDetailDepth)
		return
	}

	if request.Length <= 0 {
		request.Length = 5
	}

	h.from = strings.ToLower(request.From)
	h.to = strings.ToLower(request.To)
	h.exchangeName = request.Exchange
	h.depthLength = request.Length

	h.depth()

	if h.isDepthRunning {
		return
	}

	go func() {
		for t := range h.depthTicker.C {
			_ = t
			h.depth()
		}
	}()

	h.isDepthRunning = true
}

func (h *PairDetail) depth() {

	conn := h.Handler.ctx.GetBaseRedis().Get()
	defer conn.Close()
	depth, err := core.GetSpiderDepth(conn, h.exchangeName, h.from + "_" + h.to)
	if err != nil {
		log.Errorf("Error: %s", err)
		return
	}

	if len(depth.Asks) > h.depthLength {
		start := len(depth.Asks) - h.depthLength
		depth.Asks = depth.Asks[start:]
	}

	if len(depth.Bids) > h.depthLength {
		depth.Bids = depth.Bids[:h.depthLength]
	}

	h.Handler.response <- ws.WsResponse(depth, constants.RequestPairDetailDepth)
}

func (h *PairDetail) Init()  {
	h.exit = make(chan interface{})
	h.summaryTicker = time.NewTicker(2 * time.Second)
	h.klineTicker = time.NewTicker(30 * time.Second)
	h.depthTicker = time.NewTicker(2 * time.Second)
	go func() {
		for {
			select {
			case <-h.exit:
				h.summaryTicker.Stop()
				h.klineTicker.Stop()
				h.depthTicker.Stop()
				return
			}
		}
	}()
}

func (h *PairDetail) Clear()  {
	h.exit <- true
}

